#region The MIT License
// The MIT License
// 
// Copyright (c) 2009 octalforty studios
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Reflection;
using octalforty.Componento.ComponentModel.Shell;
using octalforty.Componento.Properties;

namespace octalforty.Componento.ComponentModel.Commands
{
    [CommandMetadata("help")]
    public class HelpCommand : ICommand
    {
        #region Private Fields
        private Lazy<ICommand, ICommandMetadata>[] commands;
        private string _name;
        private const string HelpString = "help";
        
        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets a reference to the collection of <see cref="ICommand"/> objects
        /// with associated metadata.
        /// </summary>
        [ImportMany()]
        public Lazy<ICommand, ICommandMetadata>[] Commands
        {
            [DebuggerStepThrough]
            get { return commands; }
            [DebuggerStepThrough]
            set { commands = value; }
        }

        #endregion

        #region ICommand Members
        /// <summary>
        /// Executes the command with the given arguments.
        /// </summary>
        /// <param name="args">Command line arguments</param>
        public void Execute(params string[] args)
        {
            _name = HelpString;
            if (args.Length > 1) //TODO: Use common validation
                _name = args[1];
            if (!IsKnownCommand(_name, commands))
            {
                using (new ConsoleStylingScope(ConsoleColor.Red))
                {
                    Console.WriteLine(String.Format("Can't find command {0}", _name));
                    _name = HelpString;
                }
            }
            if (_name == HelpString)
                ShowOverviewHelp();
            else
                ShowHelp();
        }

        /// <summary>
        /// Show all aviable commands with short names
        /// </summary>
        private void ShowOverviewHelp()
        {
            Console.WriteLine("Aviable commands");
            PropertyInfo[] properties = typeof(HelpMessages).GetProperties(BindingFlags.Static | BindingFlags.NonPublic);
            List<string> commandNames = new List<string>(GetCommandNames());
            foreach (PropertyInfo property in properties)
            {
                String propertyLowered = property.Name.ToLower();
                if (commandNames.Contains(propertyLowered))
                    Console.WriteLine(String.Format("{0} - {1}",
                        propertyLowered, 
                        property.GetValue(null, null)));
            }
        }

        private IEnumerable<String> GetCommandNames()
        {
            foreach (Lazy<ICommand, ICommandMetadata> command in Commands)
                yield return command.Metadata.Name.ToLower();
        }

        /// <summary>
        /// List all <paramref name="commands"/> and try to find command with <paramref name="name"/>
        /// </summary>
        /// <param name="name">Command name</param>
        /// <param name="commands">List with commands</param>
        /// <returns>Is <paramref name="name"/> known command</returns>
        private static bool IsKnownCommand(String name, Lazy<ICommand, ICommandMetadata>[] commands)
        {
            foreach (Lazy<ICommand, ICommandMetadata> command in commands)
                if (string.Compare(command.Metadata.Name, name, StringComparison.CurrentCultureIgnoreCase) == 0)
                    return true;
            return false;
        }

        private void ShowHelp()
        {
            Console.WriteLine(HelpMessages.ResourceManager.GetString(_name));
            PropertyInfo[] properties = typeof (HelpMessages).GetProperties(BindingFlags.Static | BindingFlags.NonPublic);
            String verboseName = string.Format("{0}_verbose", _name);
            foreach (PropertyInfo property in properties)
            {
                if (String.Compare(verboseName, property.Name, StringComparison.CurrentCultureIgnoreCase) == 0)
                {
                    Console.WriteLine(String.Format("Help for command {0}", _name));
                    Console.WriteLine(property.GetValue(null, null));
                }
            }
        }

        #endregion
    }
}